﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mighty.Common;

namespace Mighty.Brain
{
    public class AceBrain : BasicBrain
    {
        /// <summary>
        /// 게임시작. 매 게임마다 초기화해야할 코드가 있다면 여기에 넣어라
        /// </summary>
        public override void GameStartEvent()
        {
            ////s
        }

        /// <summary>
        /// 베팅시작, 카드를 나누어 주고 호출된다. gameInfo.myCards를 참고
        /// </summary>
        public override void BettingReadyEvent()
        {

        }

        /// <summary>
        /// 베팅차례, 지금까지 배팅된 정보(gameInfo.bettingHistory)를 참고하여 베팅을 결정한다
        /// </summary>
        /// <remarks>
        /// [기본행동] 베팅리밋(betting.limit)이 15이하 이면 베팅리밋을 +1하고 노기루다(Giruda.NoGiruda)로 베팅한다
        /// </remarks>
        public override Betting BettingTicEvent()
        {
            Betting betting = new Betting();
            Betting previousBetting = gameInfo.betting;

            betting.isPass = true;

            int[] shape = new int[4]{0, 0, 0, 0};

            for (int i = 0; i < 10; i++)
            {
                if (gameInfo.myCards[i].shape == Shape.Spade)
                {
                    shape[0]++;
                }
                else if (gameInfo.myCards[i].shape == Shape.Diamond)
                {
                    shape[1]++;
                }
                else if (gameInfo.myCards[i].shape == Shape.Heart)
                {
                    shape[2]++;
                }
                else if (gameInfo.myCards[i].shape == Shape.Clover)
                {
                    shape[3]++;
                }
            }

            int max = -1;
            int max_index = -1;
            for (int i = 0; i < 4; i++)
            {
                if (shape[i] > max)
                {
                    max = shape[i];
                    max_index = i;
                }
            }

            switch (max_index)
            {
                case 0:
                    betting.giruda = Giruda.Spade;
                    break;
                case 1:
                    betting.giruda = Giruda.Diamond;
                    break;
                case 2:
                    betting.giruda = Giruda.Heart;
                    break;
                case 3:
                    betting.giruda = Giruda.Clover;
                    break;
                default:
                    betting.giruda = Giruda.NoGiruda;
                    break;
            }

            int point = 0;

            for (int i = 0; i < 10; i++)
            {
                if ((int)gameInfo.myCards[i].shape == (int)betting.giruda)
                {
                    if (gameInfo.myCards[i].number > 10)
                        point++;
                    point++;
                }
                else if (gameInfo.myCards[i].shape == Shape.Joker)
                {
                    point += 2;
                }
                else if (gameInfo.myCards[i].number == 14)
                {
                    if (betting.giruda == Giruda.Spade)
                    {
                        if (gameInfo.myCards[i].shape == Shape.Diamond)
                        {
                            point++;
                        }
                    }
                    else if (gameInfo.myCards[i].shape == Shape.Spade)
                    {
                        point++;
                    }
                    point += 2;
                }
            }

            if (previousBetting.limit == 0)
                previousBetting.limit = 12;

            if (previousBetting.limit < 13 && point > 9 || previousBetting.limit < 14 && point > 13)
            {
                betting.limit = previousBetting.limit + 1;
                betting.isPass = false;
            }

            return betting;
        }


        /// <summary>
        /// 베팅완료, 주공이 정해졌다. 베팅이 끝났을 경우 처리할 코드가 있다면 여기에 하라.
        /// </summary>
        public override void BettingCompleteEvent()
        {
        }

        /// <summary>
        /// (주공)카드선택, 13장의 카드중 10장의 카드선택. 버릴3장의 카드에 cmdRemove=true로 설정해주어야 한다
        /// 만약 정확히 3장의 카드가 선택되지 않았을 경우에는 새로추가된 3장의 카드가 그대로 버려진다.
        /// ChoiceCardEvent 가 호출된 후에는 선택된 10장의 카드는 myCards[0]부터 차례대로 배치되고 버려진 3장의 카드는
        /// myCards[10]에서부터 배치되어 나중에 어떤 카드가 버려졌는지 알수 있게 해준다
        /// </summary>
        /// <remarks>
        /// [기본행동] 추가된 3장의 카드를 그대로 버린다
        /// </remarks>
        public override HandedCard[] ChoiceCardEvent()
        {
            int choice = 0;

            HandedCard[] choiceCards = gameInfo.myCards;

            for (int i = 10; i < 13; i++)
                if (choiceCards[i].shape == gameInfo.mighty.shape && choiceCards[i].number == gameInfo.mighty.number)
                {
                    choiceCards[i].cmdRemove = false;
                    choice++;
                }
                else if (choiceCards[i].shape == Shape.Joker)
                {
                    choiceCards[i].cmdRemove = false;
                    choice++;
                }
                else if ((int) choiceCards[i].shape == (int) gameInfo.betting.giruda)
                {
                    choiceCards[i].cmdRemove = false;
                    choice++;
                }

            while (choice-- > 0)
            {
                int min = 14;
                int min_cardnum = -1;
                for (int i = 0; i <= 9; i++)
                {
                    if (choiceCards[i].cmdRemove || choiceCards[i].shape == Shape.Joker)
                        continue;

                    if ((int) choiceCards[i].shape != (int)gameInfo.betting.giruda && choiceCards[i].number < min)
                    {
                        min = choiceCards[i].number;
                        min_cardnum = i;
                    }
                }

                if (min_cardnum == -1)
                    for (int i = 0; i <= 9; i++)
                    {
                        if (choiceCards[i].cmdRemove || choiceCards[i].shape == Shape.Joker)
                            continue;

                        if (choiceCards[i].number < min)
                        {
                            min = choiceCards[i].number;
                            min_cardnum = i;
                        }
                    }

                choiceCards[min_cardnum].cmdRemove = true;
            }

            return choiceCards;
        }

        /// <summary>
        /// (주공)프랜드선택. 프랜드를 선택하는 방법은 노프랜드(FriendPlayer.HowToSelect.nofriend),
        /// 초구프랜드(FriendPlayer.HowToSelect.firstwin),카드프랜드(FriendPlayer.HowToSelect.card)가 있다.
        /// </summary>
        /// <remarks>
        /// [기본행동] 초구프랜드를 선택한다
        /// </remarks>
        public override FriendPlayer ChoiceFriendEvent()
        {
            FriendPlayer friend = new FriendPlayer();
            
            bool mightyfriend = true;
            bool jokerfriend = true;

            for (int i = 0; i < 10; i++)
                if (gameInfo.myCards[i].shape == gameInfo.mighty.shape && gameInfo.myCards[i].number == 14)
                    mightyfriend = false;
                else if (gameInfo.myCards[i].shape == Shape.Joker)
                    jokerfriend = false;

            if (mightyfriend)
            {
                friend.howToSelect = FriendPlayer.HowToSelect.card;
                friend.cardCondition = new Card(gameInfo.mighty.shape, gameInfo.mighty.number);
                return friend;
            }
            
            if (jokerfriend)
            {
                friend.howToSelect = FriendPlayer.HowToSelect.card;
                friend.cardCondition = new Card(Shape.Joker, 14);
                return friend;
            }

            HandedCard[] h = new HandedCard[10];
            int cnt = 0;

            for (int i = 0; i < 10; i++)
            {
                if ((int)gameInfo.myCards[i].shape == (int)gameInfo.betting.giruda)
                {
                    h[cnt++] = gameInfo.myCards[i];
                }
            }

            for (int girudatop = 14; girudatop > 2; girudatop--)
            {
                bool found = false;

                for (int i = cnt - 1; i >= 0; i--)
                {
                    if (h[i].number == girudatop)
                        found = true;
                }
                
                if (!found)
                {
                    friend.howToSelect = FriendPlayer.HowToSelect.card;
                    friend.cardCondition = new Card((Shape)gameInfo.betting.giruda, girudatop);
                    return friend;
                }
            }

            friend.howToSelect = FriendPlayer.HowToSelect.firstwin;
            
            return friend;
        }

        /// <summary>
        /// 최종베팅정보, 기루다무늬변경가능. 기루다를 변경할 경우 최소+3된 limit정보를 리턴해야 한다
        /// </summary>
        /// <remarks>
        /// [기본행동] 아무런 변경도 하지 않는다
        /// </remarks>
        public override Betting LastBettingEvent()
        {
            return gameInfo.betting;
        }

        /// <summary>
        /// 각라운드의시작, 매라운드마다 초기화할 코드가 있다면 이부분에 들어가게 된다
        /// </summary>
        public override void RoundReadyEvent()
        {

        }

        /// <summary>
        /// 카드낼차례, 각 사용자의 턴에 한번씩 호출되며 낼 카드를 결과값으로 리턴해야 한다. 사용가능한 카드에는 
        /// myCards[i].isPossible 속성이 true로 설정되어 있다. 만약 사용불가능한 카드를 낼 경우에 시스템은 카드배열에서 
        /// 제일 처음 만나는 사용가능한 카드를 골라서 사용한다
        /// </summary>
        /// <remarks>
        /// [기본행동] 가능한 카드중에서 랜덤하게 하나의 카드를 선택한다
        /// </remarks>
        public override HandedCard RoundTicEvent()
        {
            //arraylist possiblecards = new arraylist();
            //foreach (handedcard card in gameinfo.mycards)
            //{
            //    if (card.ispossible) possiblecards.add(card);
            //}

            //Random r = new Random();
            //int k = r.Next(possibleCards.Count);

            if ((gameInfo.attackPlayer.name == gameInfo.myInfo.name && gameInfo.attackPlayer.position == gameInfo.myInfo.position) || (gameInfo.friend.name == gameInfo.myInfo.name && gameInfo.friend.position == gameInfo.myInfo.position))
            {
                HandedCard[] h = new HandedCard[10];

                int c = 0;

                for (int i = 0; i < 10; i++)
                    if (gameInfo.myCards[i].isPossible)
                        h[c++] = gameInfo.myCards[i];

                if (gameInfo.roundIdx == 0)
                {
                    for (int i = 0; i < c; i++)
                    {
                        if (h[i].number == 14 && h[i].shape != Shape.Joker && h[i].shape != gameInfo.mighty.shape)
                        {
                            return h[i];    // && (int) h[i].shape != (int) gameInfo.betting.giruda
                        }
                    }

                    return h[findMin(h, c)];
                }
                else if (gameInfo.roundIdx >= 1 && gameInfo.roundCardIdx == 0)
                {
                    HandedCard[] h2 = new HandedCard[10];

                    int c2 = 0;
                    for (int i = 0; i < c; i++)
                    {
                        if ((int) h[i].shape == (int) gameInfo.betting.giruda)
                        {
                            h2[c2++] = h[i];
                        }
                    }

                    if (c2 != 0)
                        return h2[findMax(h2, c2)];
                    else
                        return h[findMin(h, c)];
                }
            }

            HandedCard[] mine = new HandedCard[10];

            int cnt = 0;
            for (int i = 0; i < 10; i++)
                if (gameInfo.myCards[i].isPossible)
                    mine[cnt++] = gameInfo.myCards[i];

            int max = findMax(mine, cnt);
            int min = findMin(mine, cnt);

            if (gameInfo.roundCardIdx == 0)
                return (HandedCard)mine[min];

            if (pointcardCounter() > 0)
                if (canWin(mine, cnt))
                    if (gameInfo.roundIdx == 0 && mine[max].shape == Shape.Joker)
                        return (HandedCard)mine[(int)(min + max) / 2];
                    else
                        return (HandedCard)mine[max];

             if (gameInfo.roundIdx == 8)
                 foreach (HandedCard h in mine)
                     if (h.shape == Shape.Joker)
                         return (HandedCard)h;

      //      if (mine[min].shape == Shape.Joker)
      //          return mine[(int) (min + max) / 2];

            return (HandedCard)mine[min];
        }

        /// <summary>
        /// 각라운드의종료, 각 라운드가 종료되면 호출된다. 각 라운드의 승자는 gameinfo의 winner를 참조한다
        /// </summary>
        public override void RoundCompleteEvent()
        {

        }

        /// <summary>
        /// 게임의 종료, 게임이 종료되었을때 실행될 코드는 여기에 추가한다.
        /// </summary>
        public override void GameEndEvent()
        {
            
        }

        public int findMax(HandedCard[] card, int size)
        {
            int max = 0, index = 0;
            bool giruda = false, first = false;

            for(int i = 0; i < size; i++)
                if (card[i] == gameInfo.mighty)
                {
                    index = i;
                    break;
                }
                else if(card[i].shape == Shape.Joker)
                {
                    index = i;
                    max = 100 + 15;
                }
                else if (max < card[i].number && (int)gameInfo.betting.giruda == (int)card[i].shape)
                {
                    index = i;
                    max = card[i].number;
                    giruda = true;
                }
                else if (gameInfo.roundCardIdx != 0 && !giruda && max < card[i].number && gameInfo.roundHistory[gameInfo.roundIdx].cardStack[0].shape == card[i].shape)
                {
                    index = i;
                    max = card[i].number;
                    first = true;
                }
                else if (!giruda && !first && max < card[i].number)
                {
                    index = i;
                    max = card[i].number;
                }

            return index;
        }

        public int findMin(HandedCard[] card, int size)
        {
            int min = 16, index = 0;

            for (int i = 0; i < size; i++)
                if (min > card[i].number && card[i].number != 0)
                {
                    index = i;
                    min = card[i].number;
                }
            return index;
        }

        public int pointcardCounter()
        {
            int cnt = 0;
            for (int i = 0; i < gameInfo.roundCardIdx; i++)
                if (gameInfo.roundHistory[gameInfo.roundIdx].cardStack[i].number > 9)
                    cnt++;
            return cnt;
        }

        public bool canWin(HandedCard[] card, int size)
        {
            int max = findMax(card, size);
            for (int i = 0; i < gameInfo.roundCardIdx; i++)
            {
                HandedCard c = new HandedCard();
                c.shape = gameInfo.roundHistory[gameInfo.roundIdx].cardStack[i].shape;
                c.number = gameInfo.roundHistory[gameInfo.roundIdx].cardStack[i].number;
                if (win(c, card[max]))
                    return false;
            }
            return true;
        }

        public bool win(HandedCard a, HandedCard b)
        {
            if (a.shape == b.shape)
                if (a.number > b.number)
                    return true;
                else
                    return false;

            if (a.shape == gameInfo.mighty.shape && a.number == gameInfo.mighty.number)
                return true;
            else if (b.shape == gameInfo.mighty.shape && b.number == 14)
                return false;
            else if (a.shape == Shape.Joker && gameInfo.roundIdx != 0)
                return true;
            else if (b.shape == Shape.Joker && gameInfo.roundIdx != 0)
                return false;

            if ((int)a.shape == (int)gameInfo.betting.giruda)
                return true;
            else if ((int)b.shape == (int)gameInfo.betting.giruda)
                return false;
            else if (a.shape == gameInfo.roundHistory[gameInfo.roundIdx].cardStack[0].shape)
                return true;
            else if (a.shape == gameInfo.roundHistory[gameInfo.roundIdx].cardStack[0].shape)
                return false;
            else return false;
        }
    }
}